﻿// Inner Fire 游戏引擎库
// Shape - 物理形状
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 所属层级：应用层（4）
// 做为物理项目的形状。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-06-23

#pragma once

#include <DirectXCollision.h>
#include <game/physics/bounding_capsule.h>
#include <game/physics/collide_result.h>
#include <game/physics/collider_info.h>
#include <graphics/graphics_def.h>
#include <variant>

namespace ifire::game {
class GameObject;
class RigidBody;
class ColliderWorld;

class Collider {
public:
  // 默认构造，设置一个radius为0的粒子。
  Collider(GameObject* owner);
  Collider(const Collider& lhs) = delete;
  Collider& operator=(const Collider& lhs) = delete;
  ~Collider();

  friend class ColliderWorld;

  // 设置形状为粒子
  void SetParticle();
  // 设置形状为球型
  void SetSphere(float radius);
  // 设置形状为Box，物体的半长尺寸（非半长）
  void SetBox(IVec3 half_extents);
  // 设置形状为胶囊。明确Y轴朝向假设
  void SetCapsule(float radius, float start, float end);
  // 设置半径（同时影响Sphere和Capsule）
  void SetRadius(float radius);
  // 设置中心位置（即相对于Object的位移）
  void SetCenter(IVec3 center);
  // 通过一个Collider进行设置
  void SetCollider(ColliderInfo* info);
  // 通过一个Collider进行设置，直接使用名称指定
  void SetCollider(ikit::sstr info_name);

  void UpdateScaledInertia(float mass, IVec3 scale);
  void UpdateWorldInertia(IVec4 rotation);
  // 设置Dirty
  void MarkDirty() { bounding_dirty_ = true; }
  // 获得OBB
  const DirectX::BoundingOrientedBox& GetBoundingOrientedBox(
      const RigidBody& r) const;
  // 获得OBB
  const DirectX::BoundingOrientedBox& GetColliderBoundingOrientedBox() const;
  // 获得球体的碰撞体
  const DirectX::BoundingSphere& GetBoundingSphere(const RigidBody& r) const;
  // 获得胶囊的碰撞体
  const BoundingCapsule& GetBoundingCapsule(const RigidBody& r) const;

  // 创建一个视觉渲染项目
  GameObject* CreateVisualObject();
  // 设置视觉渲染项目
  void SetVisualObject(GameObject* visual_object);
  // 是否拥有视觉渲染对象
  bool HasVisualObject() const { return visual_object_ != nullptr; }
  // 更新视觉渲染对象
  void UpdateVisualObject();
  // 设置碰撞体生效
  void SetEnable(bool value) { enable_ = value; }
  // 碰撞体是否生效
  bool IsEnable() const { return enable_; }

  // 检查是否属于这个Mask
  bool IsLayer(uint64_t mask) const;
  // 设置此层的ID
  void SetLayer(int id) { layer_ = id; }
  // 通过名称设置此层的ID
  void SetLayer(ikit::sstr name);
  // 设置当前项目有效的Mask
  void SetMask(uint64_t mask) { mask_ = mask; };
  // 设置当前项目有效的Mask（使用名称指定）
  void SetMask(ikit::sstr name);
  // 对角色的ColliderWorld进行碰撞检查，将结果保存在列表中
  size_t CheckCharaCollision();
  // 设置碰撞的结果的上限
  void SetCollideResultLimit(size_t value) { collide_result_limit = value; }
  // 得到碰撞的结果
  size_t GetCollideResultCount() const { return collide_results_.size(); }
  // 得到碰撞的结果
  CollideResult& GetCollisionResult(int index);
  // 与另外一个碰撞体进行碰撞，如果碰撞好了，就填充碰撞结果，然后返回True。没有发生碰撞返回False
  bool Collide(Collider* target, CollideResult& result);
  // 将一个结果加入到结果列表
  void AddResult(const CollideResult& result);
  // 将此Collider送入Character的ColliderWorld
  void AddToCharaColliderWorld();

public:
  // 此碰撞体所属的层
  int layer_;
  // 此碰撞体当前的Mask
  uint64_t mask_;
  // 此碰撞体的类型
  ShapeType type;
  // 此碰撞体的相对位置
  IVec3 center;
  // 此碰撞体所属的对象
  GameObject* game_object_;
  // 此碰撞体的视觉对象
  GameObject* visual_object_;

  // 子碰撞体，用于一个物体需要多个碰撞体的情况。
  // 目前暂时不需要实现，避免系统变得复杂，等有必要时再实现。
  Collider* children = nullptr;

  union {
    SphereInfo sphere;
    IVec3 half_extents;
    CapsuleInfo capsule;
  };

  // 角动量的惯性扭矩（逆）
  IMat3 inv_inertia;
  // 惯性扭矩的世界矩阵（使用惰性更新）
  IMat3 inv_inertia_world;

  // 获得实际的位置
  IVec3 GetOffsetPosition(const IVec3& pos) const;

  // 用于保存碰撞的结果
  eastl::vector<CollideResult> collide_results_;
  // 保存碰撞的结果的数量上限
  size_t collide_result_limit = 8;

private:
  IMat3 CalculateScaledSphereInertia(float mass, IVec3 scale) const;
  IMat3 CalculateScaledBoxInertia(float mass, IVec3 scale) const;
  IMat3 CalculateScaledCapsuleInertia(float mass, IVec3 scale) const;
  // 用于缓存的Mark
  mutable bool bounding_dirty_ = true;
  // 缓存惯性动量
  mutable DirectX::BoundingOrientedBox cached_bbox_;
  mutable DirectX::BoundingSphere cached_bsphere_;
  mutable BoundingCapsule cached_bcapsule_;
  // 此碰撞体是否生效
  bool enable_;
};

} // namespace ifire::game